package com.haoma.service.impl;


import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.StrUtil;
import com.amazonaws.HttpMethod;
import com.amazonaws.services.s3.AmazonS3;
import com.amazonaws.services.s3.model.*;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.haoma.entity.Photo;
import com.haoma.entity.PhotoCategory;
import com.haoma.entity.PhotoCategoryRelation;
import com.haoma.mapper.PhotoCategoryMapper;
import com.haoma.mapper.PhotoCategoryRelationMapper;
import com.haoma.mapper.PhotoMapper;
import com.haoma.mapper.SysUploadTaskMapper;
import com.haoma.minio.MinIOConfigProperties;
import com.haoma.minio.dto.TaskInfoDto;
import com.haoma.minio.dto.TaskRecordDTO;
import com.haoma.minio.entity.UploadTask;
import com.haoma.minio.vo.InitTaskParam;
import com.haoma.service.SysUploadTaskService;
import com.haoma.upload.constant.MinioConstant;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
import org.springframework.http.MediaTypeFactory;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.net.URL;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * 分片上传-分片任务记录(SysUploadTask)表服务实现类
 *
 * @since 2022-08-22 17:47:31
 */
@Service("sysUploadTaskService")
public class SysUploadTaskServiceImpl extends ServiceImpl<SysUploadTaskMapper, UploadTask> implements SysUploadTaskService {

    @Resource
    private AmazonS3 amazonS3;

    @Resource
    private MinIOConfigProperties minioProperties;

    @Resource
    private SysUploadTaskMapper sysUploadTaskMapper;

    @Autowired
    private PhotoCategoryRelationMapper photoCategoryRelationMapper;

    @Autowired
    private PhotoMapper photoMapper;

    @Autowired
    private PhotoCategoryMapper photoCategoryMapper;

    /**
     * 获取文件唯一标识
     *
     * @param identifier
     * @return
     */
    @Override
    public UploadTask getByIdentifier(String identifier) {
        return sysUploadTaskMapper.selectOne(new QueryWrapper<UploadTask>().lambda().eq(UploadTask::getFileIdentifier, identifier));
    }


    /**
     * 初始化任务
     *
     * @param param
     * @return
     */
    @Override
    public TaskInfoDto initTask(InitTaskParam param) {
        // 创建当前时间
        Date currentDate = new Date();
        // 获取到桶名
        String bucketName = minioProperties.getBucket();
        // 文件名
        String fileName = param.getFileName();
        // 时间戳
        String suffix = fileName.substring(fileName.lastIndexOf(".") + 1, fileName.length());
        // 临时的业务key
        String blog = "video";
        // 否则文件重复这里需要重新设置因为不集中
        String key = StrUtil.format("{}/{}.{}", DateUtil.format(currentDate, "YYYY-MM-dd"), IdUtil.randomUUID(), suffix);
        // 将文件后面还需要携带/
        // 获取到当前类型
        String contentType = MediaTypeFactory.getMediaType(key).orElse(MediaType.APPLICATION_OCTET_STREAM).toString();
        // 桶的参数
        ObjectMetadata objectMetadata = new ObjectMetadata();
        // 桶的类型
        objectMetadata.setContentType(contentType);
        // 设置校验参数
        InitiateMultipartUploadResult initiateMultipartUploadResult = amazonS3
                .initiateMultipartUpload(new InitiateMultipartUploadRequest(bucketName, key).withObjectMetadata(objectMetadata));
        // 生成唯一的id
        String uploadId = initiateMultipartUploadResult.getUploadId();

        // 创建当前任务
        UploadTask task = new UploadTask();
        // 获取但分片数量
        int chunkNum = (int) Math.ceil(param.getTotalSize() * 1.0 / param.getChunkSize());
        // 设置桶名
        task.setBucketName(minioProperties.getBucket());
        // 设置分片总数量
        task.setChunkNum(chunkNum);
        // 设置总的分片大小
        task.setChunkSize(param.getChunkSize());
        // 设置总的五年级大小
        task.setTotalSize(param.getTotalSize());
        // 设置文件唯一标识
        task.setFileIdentifier(param.getIdentifier());
        // 设置文件名称
        task.setFileName(fileName);
        // 设置桶的key
        task.setObjectKey(key);
        // 设置文件的唯一id
        task.setUploadId(uploadId);
        // 上传到当前任务队列
        sysUploadTaskMapper.insert(task);


        // 向文件表存储
        Photo photo = new Photo();
        // 设置为显示状态
        photo.setState(2);
        // 将存储温江到minio中可以功资源访问
        photo.setPhotoUrl(minioProperties.getReadPath() + "/" + "blog" + "/" + key);
        // 插入到相册表中
        photoMapper.insert(photo);
        // 向关系表中存储
        PhotoCategoryRelation photoCategoryRelation = new PhotoCategoryRelation();
        // 设置分类id为7
        photoCategoryRelation.setCategoryId(7);
        // 设置相册id从数据库中获取
        photoCategoryRelation.setPhotoId(photo.getId());
        // 插入到关系表中
        photoCategoryRelationMapper.insert(photoCategoryRelation);

        // 将数量加1
        // 根据分类名称筛选
        PhotoCategory photoCategory = photoCategoryMapper.selectOne(new LambdaQueryWrapper<PhotoCategory>()
                .eq(PhotoCategory::getId, 7));
        // 将count数量加1
        photoCategory.setCount(photoCategory.getCount() + 1);

        // 将任务放回
        return new TaskInfoDto().setFinished(false).setTaskRecord(TaskRecordDTO.convertFromEntity(task)).setPath(getPath(bucketName, key));
    }

    @Override
    public String getPath(String bucket, String objectKey) {
        return StrUtil.format("{}/{}/{}", minioProperties.getEndpoint(), bucket, objectKey);
    }


    /**
     * 获取上传进度
     *
     * @param identifier
     * @return
     */
    @Override
    public TaskInfoDto getTaskInfo(String identifier) {
        // 获取文件标识
        UploadTask task = getByIdentifier(identifier);
        if (task == null) {
            return null;
        }
        // 将任务返回
        TaskInfoDto result = new TaskInfoDto()
                .setFinished(true)
                .setTaskRecord(TaskRecordDTO.convertFromEntity(task)).setPath(getPath(task.getBucketName(), task.getObjectKey()));

        // 判断桶是否存在
        boolean doesObjectExist = amazonS3.doesObjectExist(task.getBucketName(), task.getObjectKey());
        if (!doesObjectExist) {
            // 未上传完，返回已上传的分片
            ListPartsRequest listPartsRequest = new ListPartsRequest(task.getBucketName(), task.getObjectKey(), task.getUploadId());
            // 文件太大导致无法上传 视频无效
            PartListing partListing = amazonS3.listParts(listPartsRequest);
            // 需要校验是否上传过了
            result.setFinished(false).getTaskRecord().setExitPartList(partListing.getParts());
        }
        return result;
    }

    /**
     * 获取文件的校验规则生成为唯一的表示
     *
     * @param bucket    桶名
     * @param objectKey 对象的key
     * @param params    额外的参数
     * @return
     */
    public String genPreSignUploadUrl(String bucket, String objectKey, Map<String, String> params) {
        // 获取到当前时间
        Date currentDate = new Date();
        // 格式化当前时间
        Date expireDate = DateUtil.offsetMillisecond(currentDate, MinioConstant.PRE_SIGN_URL_EXPIRE.intValue());
        GeneratePresignedUrlRequest request = new GeneratePresignedUrlRequest(bucket, objectKey)
                .withExpiration(expireDate).withMethod(HttpMethod.PUT);
        if (params != null) {
            params.forEach((key, val) -> request.addRequestParameter(key, val));
        }
        URL preSignedUrl = amazonS3.generatePresignedUrl(request);
        return preSignedUrl.toString();
    }

    /**
     * 合并分片
     *
     * @param identifier
     */
    @Override
    public void merge(String identifier) {
        UploadTask task = getByIdentifier(identifier);
        if (task == null) {
            throw new RuntimeException("分片任务不存");
        }

        ListPartsRequest listPartsRequest = new ListPartsRequest(task.getBucketName(), task.getObjectKey(), task.getUploadId());
        PartListing partListing = amazonS3.listParts(listPartsRequest);
        List<PartSummary> parts = partListing.getParts();
        if (!task.getChunkNum().equals(parts.size())) {
            // 已上传分块数量与记录中的数量不对应，不能合并分块
            throw new RuntimeException("分片缺失，请重新上传");
        }
        CompleteMultipartUploadRequest completeMultipartUploadRequest = new CompleteMultipartUploadRequest()
                .withUploadId(task.getUploadId())
                .withKey(task.getObjectKey())
                .withBucketName(task.getBucketName())
                .withPartETags(parts.stream().map(partSummary -> new PartETag(partSummary.getPartNumber(), partSummary.getETag())).collect(Collectors.toList()));
        CompleteMultipartUploadResult result = amazonS3.completeMultipartUpload(completeMultipartUploadRequest);
    }
}
